/*******************************************************************************
 *  * Copyright (C), $2019-2029, OPPO Mobile Comm Corp., Ltd.
 *  * VENDOR_EDIT
 *  * File: BounceContainer.java
 *  * Description:
 *  * Version: 1.0
 *  * Date : 2019/9/20
 *  * Author: liuxin
 *  * ---------------------Revision History: ---------------------
 *  *  <author>     <date>     <version >    <desc>
 *  *  liuxin    2019/9/20       1.0        create
 ******************************************************************************/
package demo.xiaxl.com.android_demo;

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.TranslateAnimation;
import android.widget.FrameLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ScrollView;

public class BounceContainer extends FrameLayout implements ViewTreeObserver.OnGlobalLayoutListener {


    private static final String TAG = "BounceContainer";
    /**
     * 滚动时间
     */
    private static final long ANIM_TIME = 400;

    private View singleChildView;

    private Rect originalRect = new Rect();

    /**
     * 在手指滑动的过程中记录是否移动了布局
     */
    private boolean isMoved = false;

    /**
     * 如果按下时不能上拉和下拉， 会在手指移动时更新为当前手指的Y值
     */
    private float startY;

    /**
     * 阻尼
     */
    private static final float OFFSET_RADIO = 0.5f;

    private boolean isRecyclerReuslt = false;


    public BounceContainer(Context context) {
        this(context, null);
    }

    public BounceContainer(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BounceContainer(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        this.setVerticalScrollBarEnabled(false);
    }

    @Override
    protected void onFinishInflate() {
        //此处为容器中的子view，必须是RecyclerView、ListView、ScrollView之一
        final int childViewCount = getChildCount();
        if (childViewCount == 1) {
            singleChildView = getChildAt(0);
            handleScrollView(singleChildView);
        }
        if (singleChildView == null) {
            throw new RuntimeException("BounceContainer need only one child View");
        }
        //布局重绘监听，比如华为屏幕键盘可以弹出和隐藏，改变布局，加监听就可以虽键盘弹出关闭的变化而变化
        getViewTreeObserver().addOnGlobalLayoutListener(this);
        super.onFinishInflate();
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        //ScrollView中唯一的子控件的位置信息，这个位置在整个控件的生命周期中保持不变
        originalRect.set(singleChildView.getLeft(), singleChildView.getTop(), singleChildView.getRight(), singleChildView.getBottom());
    }

    /**
     * 事件分发
     */

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (singleChildView == null) {
            return super.dispatchTouchEvent(ev);
        }
        int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                //记录按下时的Y
                startY = ev.getY();
                return super.dispatchTouchEvent(ev);
            case MotionEvent.ACTION_MOVE:
                boolean isTouchOutOfScrollView = ev.getY() >= originalRect.bottom || ev.getY() <= originalRect.top;
                if (isTouchOutOfScrollView) {
                    return true;
                }
                float nowY = ev.getY();
                int scrollY = (int) (nowY - startY);
                if ((isCanPullDown() && scrollY > 0) || (isCanPullUp() && scrollY < 0) || (isCanPullDown() && isCanPullUp())) {
                    int offset = (int) (scrollY * OFFSET_RADIO);
                    singleChildView.layout(originalRect.left, originalRect.top + offset, originalRect.right, originalRect.bottom + offset);
                    isMoved = true;
                    isRecyclerReuslt = false;
                    return true;
                } else {
                    startY = ev.getY();
                    isMoved = false;
                    isRecyclerReuslt = true;
                    recoverLayout();
                    return super.dispatchTouchEvent(ev);
                }
            case MotionEvent.ACTION_UP:
                if ((singleChildView instanceof RecyclerView) || (singleChildView instanceof ListView)) {
                    boolean res = super.dispatchTouchEvent(ev);
                    if (isMoved) {
                        recoverLayout();
                    }
                    return res;
                } else {
                    if (isMoved) {
                        recoverLayout();
                        return true;
                    } else {
                        return super.dispatchTouchEvent(ev);
                    }
                }
            default:
                return true;
        }
    }

    /**
     * 位置还原
     */
    private void recoverLayout() {
        if (!isMoved) {
            return;//如果没有移动布局，则跳过执行
        }
        TranslateAnimation anim = new TranslateAnimation(0, 0, singleChildView.getTop() - originalRect.top, 0);
        anim.setDuration(ANIM_TIME);
        singleChildView.startAnimation(anim);
        singleChildView.layout(originalRect.left, originalRect.top, originalRect.right, originalRect.bottom);
        isMoved = false;
    }

    /**
     * 容器的的事件都在事件分发中处理，这里处理的是事件分发传递过来的事件，
     * <p>
     * 传递过来的为RecyclerVIew的事件  不拦截，直接交给reyclerview处理
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }


    /**
     * 判断container是否可以下拉(子view是否到顶)
     * 返回true,消费事件；返回false，事件交给子view处理
     *
     * @return
     */
    private boolean isCanPullDown() {
        if (singleChildView instanceof RecyclerView) {
            boolean res = true;
            final RecyclerView.Adapter adapter = ((RecyclerView) singleChildView).getAdapter();
            if (null == adapter || null == ((RecyclerView) singleChildView).getLayoutManager() || adapter.getItemCount() == 0) {
                return res;
            }
            final int firstVisiblePosition = ((LinearLayoutManager) ((RecyclerView) singleChildView).getLayoutManager()).findFirstVisibleItemPosition();
            if (firstVisiblePosition != 0) {
                res = false;
            } else {
                int mostTop = (((RecyclerView) singleChildView).getChildCount() > 0) ? ((RecyclerView) singleChildView).getChildAt(0).getTop() : 0;
                res = mostTop >= 0;
            }
            return res;
        } else if (singleChildView instanceof ListView) {
            final ListAdapter adapter = ((ListView) singleChildView).getAdapter();
            if (null == adapter) {
                return true;
            }
            final int firstVisiblePosition = ((ListView) singleChildView).getFirstVisiblePosition();
            if (firstVisiblePosition != 0 && adapter.getCount() != 0) {
                return false;
            }
            int mostTop = (((ListView) singleChildView).getChildCount() > 0) ? ((ListView) singleChildView).getChildAt(0).getTop() : 0;
            return mostTop >= 0;
        } else if (singleChildView instanceof ScrollView) {
            ScrollView scrollView = (ScrollView) singleChildView;
            return scrollView.getScrollY() == 0;
        } else {
            Rect r = new Rect();
            singleChildView.getLocalVisibleRect(r);
            return r.top >= 0;
        }
    }


    /**
     * 判断container是否可以上拉，即内部到底
     * 返回true,消费事件；返回false，事件交给子view处理
     *
     * @return
     */
    private boolean isCanPullUp() {
        if (singleChildView instanceof RecyclerView) {
            final RecyclerView.Adapter adapter = ((RecyclerView) singleChildView).getAdapter();
            if (null == adapter || null == ((RecyclerView) singleChildView).getLayoutManager()) {
                return true;
            }
            final int lastVisiblePosition = ((LinearLayoutManager) ((RecyclerView) singleChildView).getLayoutManager()).findLastVisibleItemPosition();
            if (lastVisiblePosition >= adapter.getItemCount() - 1) {
                final int childIndex = lastVisiblePosition - ((LinearLayoutManager) ((RecyclerView) singleChildView).getLayoutManager()).findFirstVisibleItemPosition();
                final int childCount = ((RecyclerView) singleChildView).getChildCount();
                final int index = Math.min(childIndex, childCount - 1);
                final View lastVisibleChild = ((RecyclerView) singleChildView).getChildAt(index);
                if (lastVisibleChild != null) {
                    return lastVisibleChild.getBottom() <= singleChildView.getBottom() - singleChildView.getTop();
                }
            }
            return false;
        } else if (singleChildView instanceof ListView) {
            final ListAdapter adapter = ((ListView) singleChildView).getAdapter();
            if (null == adapter) {
                return true;
            }
            final int lastVisiblePosition = ((ListView) singleChildView).getLastVisiblePosition();
            if (lastVisiblePosition >= adapter.getCount() - 1) {
                final int childIndex = lastVisiblePosition - ((ListView) singleChildView).getFirstVisiblePosition();
                final int childCount = ((ListView) singleChildView).getChildCount();
                final int index = Math.min(childIndex, childCount - 1);
                final View lastVisibleChild = ((ListView) singleChildView).getChildAt(index);
                if (lastVisibleChild != null) {
                    return lastVisibleChild.getBottom() <= singleChildView.getBottom() - singleChildView.getTop();
                }
            }
            return false;
        } else if (singleChildView instanceof ScrollView) {
            ScrollView scrollView = (ScrollView) singleChildView;
            View contentView = scrollView.getChildAt(0);
            Log.i(TAG,"left:"+contentView.getMeasuredHeight()+"     Right:" +(scrollView.getScrollY() + scrollView.getHeight()));
            return (contentView.getMeasuredHeight() <= (scrollView.getScrollY() + scrollView.getHeight()));
        } else {
            Rect r = new Rect();
            singleChildView.getLocalVisibleRect(r);
            return r.bottom <= singleChildView.getBottom() - singleChildView.getTop();
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public void onGlobalLayout() {
        requestLayout();
        getViewTreeObserver().removeOnGlobalLayoutListener(this);
    }

    private void handleScrollView(View singleChildView) {
        if (singleChildView instanceof RecyclerView) {
            RecyclerView recyclerView = (RecyclerView) singleChildView;
            recyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
                @Override
                public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
                    return isMoved;
                }

                @Override
                public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {

                }

                @Override
                public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

                }
            });
        }
        if (singleChildView instanceof ListView) {
            ListView listView = (ListView) singleChildView;
            listView.setOnTouchListener(new OnTouchListener() {

                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    return isMoved;
                }
            });
        }
        if (singleChildView instanceof ScrollView) {
            ScrollView scrollView = (ScrollView) singleChildView;
            scrollView.setOnTouchListener(new OnTouchListener() {

                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    return isMoved;
                }
            });
        }
    }

}
